package cn.minhx.mw.dtp.sdk.config;

import cn.minhx.mw.dtp.sdk.domain.DynamicThreadPoolService;
import cn.minhx.mw.dtp.sdk.domain.DynamicThreadPoolServiceImpl;
import cn.minhx.mw.dtp.sdk.domain.model.entity.ThreadPoolConfigEntity;
import cn.minhx.mw.dtp.sdk.domain.model.valobj.RegistryEnumVO;
import cn.minhx.mw.dtp.sdk.registry.Registry;
import cn.minhx.mw.dtp.sdk.registry.redis.RedisRegistryImpl;
import cn.minhx.mw.dtp.sdk.trigger.listener.ThreadPoolConfigAdjustListener;
import cn.minhx.mw.dtp.sdk.trigger.job.ThreadPoolConfigReportJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author minhx
 */
@Slf4j
@EnableScheduling
@AutoConfiguration
@EnableConfigurationProperties({DynamicThreadPoolProperties.class})
public class DynamicThreadPoolAutoConfiguration {

    private static String applicationName;

    @Bean("dynamicThreadPoolRedissonClient")
    public RedissonClient redissonClient(DynamicThreadPoolProperties properties) {
        Config config = new Config().setCodec(JsonJacksonCodec.INSTANCE);
        config.useSingleServer()
                .setAddress("redis://" + properties.getHost() + ":" + properties.getPort())
                .setDatabase(properties.getDatabase())
                .setPassword(properties.getPassword())
                .setConnectionPoolSize(properties.getPoolSize())
                .setConnectionMinimumIdleSize(properties.getMinIdleSize())
                .setIdleConnectionTimeout(properties.getIdleTimeout())
                .setConnectTimeout(properties.getConnectTimeout())
                .setRetryAttempts(properties.getRetryAttempts())
                .setRetryInterval(properties.getRetryInterval())
                .setPingConnectionInterval(properties.getPingInterval())
                .setKeepAlive(properties.isKeepAlive());
        RedissonClient redissonClient = Redisson.create(config);
        log.info("[动态线程池-初始化]注册中心 redis 初始化完成 {}:{} health={}", properties.getHost(), properties.getPort(), !redissonClient.isShutdown());
        return redissonClient;
    }

    @Bean("dynamicThreadPoolService")
    public DynamicThreadPoolServiceImpl dynamicThreadPoolService(ApplicationContext applicationContext, Map<String, ThreadPoolExecutor> threadPoolExecutorMap, RedissonClient redissonClient) {
        applicationName = applicationContext.getEnvironment().getProperty("spring.application.name");
        if (StringUtils.isBlank(applicationName)) {
            applicationName = "缺省";
            log.debug("[动态线程池-初始化]未配置 spring.application.name ！");
        }
        // 获取缓存数据，设置本地线程池配置【应用启动时，检查是否有历史配置】
        threadPoolExecutorMap.forEach((threadPoolName, threadPoolExecutor) -> {
            String key = RegistryEnumVO.DTP_THREAD_POOL_CONFIG.getKey() + "_" + applicationName + "_" + threadPoolName;
            ThreadPoolConfigEntity config = redissonClient.<ThreadPoolConfigEntity>getBucket(key).get();
            if (Objects.isNull(config)) {
                return;
            }
            log.debug("[动态线程池-初始化]检测到历史配置，使用历史数据覆盖本地线程池配置 threadPoolName={} corePoolSize={} maximumPoolSize={}",
                    threadPoolName, config.getCorePoolSize(), config.getMaximumPoolSize());
            threadPoolExecutor.setCorePoolSize(config.getCorePoolSize());
            threadPoolExecutor.setMaximumPoolSize(config.getMaximumPoolSize());
        });
        // 注入 bean
        return new DynamicThreadPoolServiceImpl(applicationName, threadPoolExecutorMap);
    }

    @Bean(name = "dynamicThreadRedisRegistry")
    public Registry redisRegistry(RedissonClient dynamicThreadRedissonClient) {
        return new RedisRegistryImpl(dynamicThreadRedissonClient);
    }

    @Bean(name = "threadPoolConfigReportJob")
    public ThreadPoolConfigReportJob threadPoolDataReportJob(DynamicThreadPoolService dynamicThreadPoolService, Registry registry) {
        return new ThreadPoolConfigReportJob(dynamicThreadPoolService, registry);
    }

    @Bean(name = "threadPoolConfigAdjustListener")
    public ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener(DynamicThreadPoolService dynamicThreadPoolService, Registry registry) {
        return new ThreadPoolConfigAdjustListener(dynamicThreadPoolService, registry);
    }

    @Bean(name = "threadPoolConfigAdjustTopic")
    public RTopic threadPoolConfigAdjustListener(RedissonClient redissonClient, ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener) {
        RTopic topic = redissonClient.getTopic(RegistryEnumVO.DTP_THREAD_POOL_CONFIG_PUBSUB_TOPIC.getKey() + "_" + applicationName);
        topic.addListener(ThreadPoolConfigEntity.class, threadPoolConfigAdjustListener);
        return topic;
    }

}
